/**
*  \file cant.c
*
*  \brief Driver interface to Vector CAN-Cards.
*
*  \author Michael J. Hohmann <michael.hohmann@linde-mh.de>
*
* 	The module cant.c provide an interface to access CAN-Card from Vector.
*   \todo Find callback functions for received ID is done inefficient and should may
* 			replaced by an hash table.
*
*/

/*===========================[ private includes  ]============================*/
#include <stdio.h>
#include "cant.h"

/*===========================[ private defines   ]============================*/
/** This value is taken from the Vector examples.
 *  it seems not to be save to change this value.
 */
#define RECEIVE_EVENT_SIZE 1

/*===========================[ private datatypes ]============================*/

/*===========================[ private variables ]============================*/
/**  Contains the hardware configuration */
static 	XLdriverConfig  cant_xlDrvConfig;

/** Indicate that CAN-Driver DLL is loaded */
static 	BO 	cant_driver_started=FALSE;

/*===========================[ public variables  ]============================*/

/*===========================[ private functions ]============================*/
static RC cant_start_driver(void);
static XLstatus CreateRxThread(struct cant_obj *me);
static RC cant_get_channel_info(struct cant_obj* me, CH* application_name, U16 logic_channel );

/*============================================================================*/

/**
*  Create channel object.
*
*  The function cant_create() loads the Vector can driver DLL and opens an port
*  with the requested properties. A struct with type cant_obj will be allocated
*  from heap and initialized by the call of the function cant_initialize().
*
*  \param BaudRate	Desired baud rate
*  \param name	    Zero terminated string with the name of the application in the Vector hardware config panel.
*  \param channel 	Logic CAN-Channel to be used.
*
*  \return Pointer to the newly created cant_obj object or NULL if object can't be created.
*/
struct cant_obj * cant_create(unsigned int BaudRate, CH *name, U16 channel )
{
	struct cant_obj *me;

	me=malloc( sizeof( struct cant_obj ) ); /* try to allocate object from heap */

	if( NULL == me )
	{
	   return NULL;   /* exit with NULL if allocating fails */
   }

	/* clear allocated memory */
	memset(me,0,sizeof(struct cant_obj));

	if( RC_SUCCESS!=cant_construct(me,BaudRate,name, channel ))
	{ /* If initialization fails, free memory and return NULL */
		free(me);
		me=NULL;
	}

	return me;
}

/** Object Initialization.
 *
 * The function cant_initialize() initialize the passed object with the passed properties.
 * This function has to be called before the cant_obj object is used.
 * It is may not save to initialize one object twice, at least one driver handle will not be closed.
 * cant_initialize() will be called from the function cant_create() to initialize the memory object
 * allocated from heap. But it is also useful to call this function with an static variable of
 * cant_obj for example if dynamic memory allocation is not wanted.
 * \param me Pointer to an not initialized cant_obj struct.
 * \param BaudRate Can Bus speed.
 * \param name Null terminated string which contain device description.
 *
 * \return RC_SUCCESS if successful otherwise RC_ERROR
 *
 * \todo Length of name has to be checked.
 * */
RC cant_construct(struct cant_obj* me, unsigned int BaudRate,CH *name, U16 logic_channel )
{
	XLstatus        xlStatus;

	/* first try to open the Vector hardware, i.e. start the driver */
	if(RC_SUCCESS != cant_start_driver())
	{
	   return RC_ERROR;
	}

	me->BaudRate=BaudRate;
	me->xlPortHandle=XL_INVALID_PORTHANDLE;

	/* set channel & permission masks by reading channel information from the "Vector Hardware" control panel */
	if (RC_SUCCESS != cant_get_channel_info(me, name, logic_channel ))
	{
	   return RC_ERROR;
	}

	/* open ONE port including all channels */
	xlStatus = xlOpenPort(&me->xlPortHandle, name, me->xlChannelMask, &me->xlPermissionMask, 256, XL_INTERFACE_VERSION, XL_BUS_TYPE_CAN);

  	if ( (XL_SUCCESS == xlStatus) && (XL_INVALID_PORTHANDLE != me->xlPortHandle) )
  	{

		/* if we have permission we set the bus parameters (baudrate) */
		if (me->xlPermissionMask)
		{
		  xlStatus = xlCanSetChannelBitrate(me->xlPortHandle, me->xlChannelMask, me->BaudRate);
		}
  	}

	if((XL_SUCCESS != xlStatus)  || (XL_INVALID_PORTHANDLE == me->xlPortHandle))
	{
    	xlClosePort(me->xlPortHandle);
    	me->xlPortHandle = XL_INVALID_PORTHANDLE;
    	xlStatus = XL_ERROR;
  	}

	xlStatus = xlGetDriverConfig(&cant_xlDrvConfig);

	me->serial_number=cant_xlDrvConfig.channel[me->channel_index].serialNumber;

 	CreateRxThread(me);

   	xlStatus = xlActivateChannel(me->xlPortHandle, me->xlChannelMask, XL_BUS_TYPE_CAN, XL_ACTIVATE_RESET_CLOCK);

	return XL_SUCCESS== xlStatus ? RC_SUCCESS:RC_ERROR;
}


/** Install Receive Callback.
 *
 * The function cant_install_rx_callback() install a callback function and
 * additional callback data to an receiver ID of the CAN-Message.
 * \param me Pointer to an existing and initialized cant_obj object instance.
 * \param id CAN-Id which will be associated with the callback function after a call.
 * \param callback Function pointer of a callback function. The function have to be from
 *  type cant_rx_callback_function_t which has the following form
 *  void* cant_rx_callback_function_t(struct cant_obj * me, struct cant_msg* msg, void** data).
 * \param data Data which will be passed to the callback function.
 *
 * \return Always SUCCESS
 *
 */
RC cant_install_rx_callback( struct cant_obj* me, unsigned int id, cant_rx_callback_function_t callback, void * data )
{
	if( CANT_ID_TABLE_SIZE <= id )
	{
		return RC_ERROR_RANGE;
	}

	me->rx_dispatcher[id].function=callback;
	me->rx_dispatcher[id].data=data;

	return RC_SUCCESS;
}

RC cant_install_default_rx_callback( struct cant_obj* me, cant_rx_callback_function_t callback, void * data )
{
	me->rx_default_callback.function = callback;
	me->rx_default_callback.data = data;

	return RC_SUCCESS;
}



/** Set Channel Activation.
 *
 * The function cant_activate() activate or deactivate the Driver channel dependig on the passed value.
 * \param me Pointer to an existing and initialized cant_obj object instance.
 * \param value	False will deactivate channel, all other values will activate channel.
 *
 * \return RC_SUCCESS if operation was successful otherwis RC_ERROR.
 */

RC cant_activate(struct cant_obj* me, BO value)
{
	XLstatus        xlStatus;

	if(value)
	{
 		xlStatus = xlActivateChannel(me->xlPortHandle, me->xlChannelMask, XL_BUS_TYPE_CAN, XL_ACTIVATE_RESET_CLOCK);
 		if (XL_SUCCESS == xlStatus) {
 		   xlCanSetReceiveMode(me->xlPortHandle, 1, 0);
 		}
  	}
  	else
  	{
    	xlStatus = xlDeactivateChannel(me->xlPortHandle,me->xlChannelMask);
  	}

	return ( XL_SUCCESS == xlStatus ) ? RC_SUCCESS : RC_ERROR;
}


/** Transmit CAN Message
 *
 * The function cant_tx() send the passed message to the also passed channel.
 * \param me Pointer to the cant_obj object.
 * \param msg Pointer to an cant_msg CanMessage object.
 *
 * \return RC_SUCCESS if operation was successful otherwise RC_ERRROR.
 * */
RC cant_tx(struct cant_obj *me, struct canmsg *msg)
{
	static XLevent       xlEvent;
	XLstatus             xlStatus;
	unsigned int         messageCount = 1;

	memset(&xlEvent, 0, sizeof(xlEvent));

  	xlEvent.tag                 = XL_TRANSMIT_MSG;
  	xlEvent.tagData.msg.id      = canmsg_get_id( msg );
  	xlEvent.tagData.msg.dlc     = canmsg_get_dlc( msg );
  	xlEvent.tagData.msg.flags   = 0;
  	/* copy datafield from cant_msg into XLevent. */
  	memcpy(&xlEvent.tagData.msg.data[0],&canmsg_data_ptr( msg )[0],canmsg_get_dlc( msg ));
  	xlStatus = xlCanTransmit(me->xlPortHandle, me->xlChanMaskTx, &messageCount, &xlEvent);

	return ( XL_SUCCESS == xlStatus ) ? RC_SUCCESS : RC_ERROR;
}

/**
 *  Receive Thread.
 *
 *  The function RxThread() will called from Windows System Event Manger.
 *  \param par It is expected thas this void pointer points to a reference of an cant_obj object.
 * 					 The value is set in CreateRxThread function.
 *
 * \return Always NO_ERROR.
 */
static DWORD WINAPI RxThread(LPVOID par)
{
  	XLstatus        xlStatus;
  	struct cant_obj *me = (struct cant_obj *)par; /* just botch the pointer to the expected object.*/

  	me->RXThreadRun = 1;

  	while (me->RXThreadRun)
  	{

  		WaitForSingleObject(me->hMsgEvent,INFINITE);

    	xlStatus = XL_SUCCESS;

   	 	while (xlStatus==XL_SUCCESS)
    	{

      		unsigned int    msgsrx = RECEIVE_EVENT_SIZE;
      		XLevent         xlEvent;

      		xlStatus = xlReceive(me->xlPortHandle, &msgsrx, &xlEvent);

			if( (XL_SUCCESS == xlStatus) &&
				( xlEvent.tag == XL_RECEIVE_MSG ) &&
				( msgsrx == 1 )&&
				((xlEvent.tagData.msg.flags&~XL_CAN_MSG_FLAG_OVERRUN) == 0) /* accept only the overrun flag */
			   )
	  		{
				struct canmsg msg;

	  			memcpy(&canmsg_data_ptr( &msg )[0],&xlEvent.tagData.msg.data[0], xlEvent.tagData.msg.dlc);

	  			canmsg_set_id( &msg , xlEvent.tagData.msg.id ) ;
	  			canmsg_set_dlc( &msg , xlEvent.tagData.msg.dlc );;

	  			/* look if callbackfunktion is assigned */
	  			if (( CANT_ID_TABLE_SIZE > canmsg_get_id( &msg ) ) && (me->rx_dispatcher[canmsg_get_id( &msg )].function))
	  			{
					/* This is somehow tricky.
					 * In short it has the shape of
					 * fp = fp( msg, data );
					 * where fp is an function pointer. So that the new value of fp is the
					 * return value of the executed function.
					 * Fine - but what is it good for ? This special method usually will be used
					 * to create an state machine, where all states are encapsulated in functions
					 * and the next state will be returned.
					 * For our application it is possible for the function to tell the caller,
					 * what function should be called the next time. If the function return NULL
					 * no function will be called anymore.
					 */
	  				 me->rx_dispatcher[canmsg_get_id( &msg )].function(me,&msg, me->rx_dispatcher[canmsg_get_id( &msg )].data);
	  			}
	  			if( NULL != me->rx_default_callback.function  )
	  			{
	  				me->rx_default_callback.function(me,&msg,me->rx_default_callback.data);
	  			}
	  			if ( xlStatus!=XL_ERR_QUEUE_IS_EMPTY )
	  			{
        			/* ResetEvent(g_hMsgEvent); */
      			}
	  		}
    	}
    }
  	return NO_ERROR;
}

/** Close Channel.
 *
 * The function cant_close() close associated port handle,
 * and stop installed rxThread.
 *
 * \param me Pointer to an existing and initialized cant_obj object instance.
 * \return Always RC_SUCCESS
 * */
RC cant_close(struct cant_obj *me)
{
   	/* try to close port handle*/
   	xlClosePort(me->xlPortHandle);
   	me->xlPortHandle = XL_INVALID_PORTHANDLE;

	/* stop thread */
	me->RXThreadRun=FALSE;

	return RC_SUCCESS;
}

/** Destructor.
 *
 * The function cant_destro() close associated port handle and rxThread
 * by calling cant_close().
 * Then the allocated memory will be freed.
 * \attention the call of this function is only allowed for objects
 * created by cant_create().
 *
 * \param me Pointer to an existing and initialized cant_obj object instance.
 * \return Always RC_SUCCESS;
 */
RC cant_destroy(struct cant_obj *me)
{
	cant_close(me);
	free(me);
	return RC_SUCCESS;
}

/** Flush queues.
 *
 * This function flushes the receive and transmit queues of the CAN-interface associated with 'me'. The Vector CAN-
 * API documentation does not clearly specify whether 'flush' means 'clear' or 'send all messages'.
 *
 * \param   me Pointer to an existing and initialized cant_obj object instance.
 * \return  RC_SUCCESS  if both the transmit and receive queue could be successfully flushed \n
 *          RC_ERROR    if not
 *
 */
RC cant_flushQueues(struct cant_obj* me) {
   XLstatus ec1 = xlCanFlushTransmitQueue(me->xlPortHandle, me->xlChanMaskTx);
   XLstatus ec2 = xlFlushReceiveQueue(me->xlPortHandle);
   if (ec1 == XL_SUCCESS && ec2 == XL_SUCCESS) {
      return RC_SUCCESS;
   } else {
      return RC_ERROR;
   }
}

/**
 * Create Receive Thread.
 * The function CreateRxThread() create a new Thread and set Notification messages.
 *
 * \param me Pointer to the cant_obj object.
 * \note its not save to pass NULL.
 */
static XLstatus CreateRxThread(struct cant_obj *me)
{
  XLstatus      xlStatus = XL_ERROR;
  DWORD         ThreadId=0;

  if (me->xlPortHandle!= XL_INVALID_PORTHANDLE) {

      /* Send a event for each Msg */
      xlStatus = xlSetNotification (me->xlPortHandle, &me->hMsgEvent, 1);

      me->hRXThread = CreateThread(0, 0x1000, RxThread, (LPVOID) me, 0, &ThreadId);

  }
  return xlStatus;
}

/**
 * Start Driver.
 *
 * The function cant_start_driver load the DLL, load all symbols, and read the driver configuration.
 * It use the static module variable cant_driver_started to protect DLL to be opened several times.
 * \note it is save to call this function several times, the driver is started once.
 *
 * \return RC_SUCCESS if operation was successful, otherwise RC_ERROR.
 */
static RC cant_start_driver(void)
{
	XLstatus xlStatus;

	if( FALSE != cant_driver_started )
		return RC_SUCCESS;

	xlStatus = xlOpenDriver();

  	if(XL_SUCCESS != xlStatus)
  		return RC_ERROR;

   	xlStatus = xlGetDriverConfig(&cant_xlDrvConfig);

 	if(XL_SUCCESS != xlStatus)
  		return RC_ERROR;

	cant_driver_started=TRUE;

	return RC_SUCCESS;
}

/** Set channel index and channel access masks according to the "Vector Hardware" config.
 *
 * This function reads the application configuration from the "Vector Hardware" config panel in the Windows control
 * panel and sets the correct channel index and channel masks according to the selected hardware channel. A new
 * configuration entry in the "Vector Hardware" config panel is created for the application if no entry exists.
 *
 * \param   me    Pointer to the cant_obj struct whose channel index and masks should be set.
 * \param   name  Name of the application. This name is also used for the config entry in the "Vector Hardware" panel.
 * \return	RC_SUCCESS 	If the channel info could be set properly <br>
 *          RC_ERROR    If not.
 */
static RC cant_get_channel_info(struct cant_obj* me, CH* application_name, U16 logic_channel )
{
   RC return_code = RC_ERROR;       /* return code for this function */
   XLstatus xlStatus = XL_ERROR;    /* return code from other called functions */
   unsigned int hw_type=0;
   unsigned int hw_index;
   unsigned int hw_channel;

   /* try to retrieve application configuration from "Vector Hardware" panel in the Windows control panel */
   xlStatus = xlGetApplConfig(application_name, logic_channel, &hw_type, &hw_index, &hw_channel, XL_BUS_TYPE_CAN);


   if (XL_SUCCESS != xlStatus)
   {
      if (XL_ERROR == xlStatus)
      {
         /* "xlGetApplConfig()" returns "XL_ERROR" if no application settings could be read from the control panel.
            If that happens, create default application settings which the user can modify afterwards. */
         xlStatus = xlSetApplConfig(application_name,       /* application name */
        		 	 	 	 	 	 logic_channel,     	/* how many bus channels does this application support */
									 hw_type,         		/* selected type of interface (Cancard, VN1630, ...) */
									 hw_index,      		/* choose first interface that is of the preferred type */
									 hw_channel,      		/* choose first channel of the first preferred interface */
									 XL_BUS_TYPE_CAN    );  /* type of bus that this application uses */
      }

      return_code = RC_ERROR;
   }
   else
   {
      /* calculate and set channel index number */
      me->channel_index    = xlGetChannelIndex((int)hw_type, (int)hw_index, (int)hw_channel);

      /* calculate and set channel mask */
      me->xlChannelMask    = xlGetChannelMask((int)hw_type, (int)hw_index, (int)hw_channel);
      me->xlPermissionMask = me->xlChannelMask;
      me->xlChanMaskTx     = me->xlChannelMask;

      return_code = RC_SUCCESS;
   }

   return return_code;
}

/*******************************************************************************
*       Copyright 2006-2018 Linde Material Handling. All rights reserved.      *
*******************************************************************************/
















